package com.moonciki.artadmin.core.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.TypeUtils;
import com.moonciki.artadmin.core.enums.ResponseEnum;
import com.moonciki.artadmin.core.exception.CustomException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 公共工具类
 *
 * @author ysq
 */
@Slf4j
public class CommonUtil {

    static volatile String timeStr;
    static AtomicInteger tsn = new AtomicInteger(0);

    /**
     * 生成随机数，包含起始
     *
     * @param min
     * @param max
     * @return
     */
    public static int getRundomNumber(int min, int max) {
        if(max < min){
            throw CustomException.createException(ResponseEnum.request_error.info());
        }

        Random random = new Random();

        int result = random.nextInt(max - min + 1) + min;

        return result;
    }

    /**
     * 生成唯一 id
     * 支持毫秒万以内
     * @return
     */
    public synchronized static String getUniqueKey() {

        String dayType = "yyyyMMddHHmmssSSSS";
        String timeStamp = DateUtil.getNowTime(dayType);

        int tsnTmp = 0;
        if (!timeStamp.equals(timeStr)) {
            timeStr = timeStamp;
            tsn.set(0);
        } else {
            tsnTmp = tsn.incrementAndGet();
        }

        String tsnNumber = String.format("%03d", tsnTmp);

        String reuslt = timeStamp + tsnNumber;

        return reuslt;
    }

    /**
     * 获得 文本文件内容
     *
     * @param path
     * @return
     */
    public static String getFileContent(String path) {
        StringBuilder builder = new StringBuilder();

        FileReader fr = null;
        BufferedReader reader = null;
        try {
            fr = new FileReader(path);
            reader = new BufferedReader(fr);
            String line = null;

            int lineCount = 0;

            while ((line = reader.readLine()) != null) {
                if(lineCount > 0){
                    builder.append("\r\n");
                }
                builder.append(line);

                lineCount++;
            }

            reader.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            log.error("", e);
        }finally {
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
            if(fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
        }
        return builder.toString();
    }

    /**
     * 将文本写入文件
     *
     * @param path
     * @param content
     * @param append
     */
    public static void writeFileContent(String path, String content, boolean append) {

        if(content == null){
            return;
        }

        createFileFolder(path);

        FileWriter fw = null;
        BufferedWriter writer = null;
        try {
            fw = new FileWriter(path, append);
            writer = new BufferedWriter(fw);

            writer.write(content);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            log.error("", e);
        } finally {
            try {
                writer.close();
                fw.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                log.error("", e);
            }
        }
    }

    /**
     * 将数据写入文件
     *
     * @param path
     * @param dataByte
     * @param append
     */
    public static void writeFile(String path, byte[] dataByte, boolean append) {

        createFileFolder(path);

        OutputStream output = null;
        try {
            output = new FileOutputStream(path, append);

            output.write(dataByte);
        } catch (Exception e) {
            throw CustomException.createException(e);
        } finally {
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                    throw CustomException.createException(e);
                }
            }
        }
    }

    /**
     * byte 数组拼接追加
     *
     * @param byteArrayList
     * @return
     */
    public static byte[] joinAllBytes(byte[]... byteArrayList) throws Exception {
        byte[] resultBytes = null;

        ByteArrayOutputStream byteOut = null;

        byteOut = new ByteArrayOutputStream();
        try {
            for (byte[] oneArray : byteArrayList) {
                byteOut.write(oneArray);
            }

            resultBytes = byteOut.toByteArray();

            byteOut.reset();

        } finally {
            if (byteOut != null) {
                try {
                    byteOut.close();
                } catch (IOException e) {
                    log.error("", e);
                }
            }
        }

        return resultBytes;
    }

    /**
     * 读取文件
     *
     * @param path
     * @return
     */
    public static byte[] readFileData(String path) {
        byte[] resultByte = null;

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        InputStream input = null;
        try {
            input = new FileInputStream(path);

            int readLength = 0;

            byte[] tmpByte = new byte[4 * 1024];

            while ((readLength = input.read(tmpByte)) > 0) {

                byteOut.write(tmpByte, 0, readLength);

            }

            resultByte = byteOut.toByteArray();

        } catch (Exception e) {
            throw CustomException.createException(e);

        } finally {
            try {
                input.close();

                byteOut.close();
            } catch (IOException e) {
                log.error("", e);
            }
        }

        return resultByte;
    }

    /**
     * 创建文件包括目录
     *
     * @param pathFile
     * @return
     * @throws IOException
     */
    public static void createFile(File pathFile) throws IOException {
        File parentFile = pathFile.getParentFile();

        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
        if (!pathFile.exists()) {
            pathFile.createNewFile();
        }
    }

    /**
     * 创建文件包括目录
     * @param filePath
     * @return
     * @throws IOException
     */
    public static void createFileFolder(String filePath) {

        File parentFile = new File(filePath).getParentFile();

        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }
    }

    /**
     * 创建文件包括目录
     *
     * @param path
     * @return
     * @throws IOException
     */
    public static File createFile(String path) throws IOException {
        File resultFile = new File(path);

        createFile(resultFile);

        return resultFile;
    }

    /**
     * 是否都为空
     * @param objArray
     * @return
     */
    public static boolean isAllNull(Object ... objArray){
        boolean result = true;

        for(Object obj : objArray){

            if(obj != null){
                result = false;

                break;
            }

        }
        return result;
    }

    /**
     * 是否包含空
     * @param objArray
     * @return
     */
    public static boolean hasNull(Object ... objArray){
        boolean result = false;

        for(Object obj : objArray){

            if(obj == null){
                result = true;

                break;
            }

        }
        return result;
    }

    /**
     * 将 long set 转成字符串
     *
     * @param longSet
     * @return
     */
    public static String joinLongSet(Set<Long> longSet) {
        StringBuilder builder = new StringBuilder();

        int i = 0;
        for (Long oneLong : longSet) {
            if (i > 0) {
                builder.append(",");
            }
            builder.append(oneLong);
            i++;
        }
        return builder.toString();
    }

    /**
     * 获取不带后缀的文件名
     *
     * @param fileName
     * @return
     */
    public static String getFileNameWithoutSuffix(String fileName) {
        String result = fileName;

        int index = fileName.lastIndexOf(".");

        if (index >= 0) {
            result = fileName.substring(0, index);
        }

        return result;
    }

    /**
     * 转换
     *
     * @param content
     * @param objClass
     * @return
     */
    public static Object parseContentToObj(Object content, Class objClass) {

        String valueJson = JSON.toJSONString(content);
        Object parseObj = JSON.parseObject(valueJson, objClass);

        return parseObj;
    }

    /**
     * 正则校验
     *
     * @param reg
     * @param text
     * @return
     */
    public static boolean regMatch(String reg, String text) {
        Pattern pattern = Pattern.compile(reg);
        Matcher matcher = pattern.matcher(text);

        boolean result = matcher.matches();

        return result;
    }

    /**
     * 用冒号(:)拼接 redis key
     *
     * @param strArray
     * @return
     */
    public static String joinRedisKey(String... strArray) {
        StringBuilder builder = new StringBuilder();

        int i = 0;
        boolean endwithSplit = false;
        for (String onePart : strArray) {

            if (i > 0 && !endwithSplit) {
                builder.append(":");
            }

            builder.append(onePart);

            if (onePart.endsWith(":")) {
                endwithSplit = true;
            } else {
                endwithSplit = false;
            }

            i++;
        }

        return builder.toString();
    }

    /**
     * 生成uuid
     *
     * @param nonSplit 是否过滤分隔符
     * @return
     */
    public static String getUUID(boolean nonSplit) {
        String uuid = UUID.randomUUID().toString().toUpperCase();

        if (nonSplit) {
            uuid = uuid.replaceAll("-", "");
        }

        return uuid;
    }

    /**
     * 获得文件后缀
     *
     * @param fileName
     * @return
     */
    public static String getFileSuffix(String fileName) {
        String suffix = "";

        if (CommonUtil.isBlank(fileName)) {
            return suffix;
        }

        File tmpFile = new File(fileName);

        String nameOnly = tmpFile.getName();

        int index = nameOnly.lastIndexOf(".");

        if (index >= 0) {
            suffix = nameOnly.substring(index).toLowerCase();
        }
        return suffix;
    }

    /**
     * 拼接文件路径
     * 统一用 Linx 路径分隔符
     *
     * @param partArray
     * @return
     */
    public static String joinPath(String... partArray) {
        String fullPath = null;

        if (partArray != null) {
            fullPath = "";

            for (String onePart : partArray) {
                if (CommonUtil.isNotBlank(onePart)) {

                    onePart = onePart.replace('\\', '/');

                    if (fullPath.length() > 0) {

                        if(onePart.startsWith("/")){
                            onePart = onePart.replaceAll("^/+", "");
                        }
                    }
                    if(CommonUtil.isNotBlank(onePart)){
                        if (fullPath.length() > 0) {
                            if (!fullPath.endsWith("/")) {
                                fullPath = fullPath + "/";
                            }
                        }
                        fullPath = fullPath + onePart;
                    }
                }
            }
        }

        return fullPath;
    }

    /**
     * 将对象转成map
     *
     * @param obj
     * @return
     */
    public static Map<String, Object> objectToMap(Object obj) {
        Map<String, Object> result = null;

        if (obj != null) {
            String json = JSON.toJSONString(obj);

            result = JSON.parseObject(json, new TypeReference<Map<String, Object>>() {
            });
        }

        return result;
    }

    /**
     * 将对象转为restTemplete 对象
     * @param obj
     * @return
     */
    public static MultiValueMap<String, String> objectToMultiMap(Object obj){
        Map<String, String> result = null;

        MultiValueMap<String, String> multiMap = new LinkedMultiValueMap<>();
        if (obj != null) {
            String json = JSON.toJSONString(obj);

            result = JSON.parseObject(json, new TypeReference<Map<String, String>>() {
            });

            for(Map.Entry<String, String> entry: result.entrySet()) {
                multiMap.add(entry.getKey(), entry.getValue());
            }
        }
        return multiMap;
    }

    /**
     * 删除文件及下级所有文件
     *
     * @param path
     * @return
     */
    public static boolean deleteFile(String path) {
        File file = new File(path);
        //判断是否待删除目录是否存在
        if (!file.exists()) {
            return false;
        }

        if (file.isDirectory()) {
            //取得当前目录下所有文件和文件夹
            File[] content = file.listFiles();

            for (File temp : content) {
                //递归调用，删除目录里的内容
                deleteFile(temp.getAbsolutePath());
            }
        }

        file.delete();

        return true;
    }

    /**
     * 拆分逗号分隔的id
     *
     * @param idArrayStr
     */
    public static List<Long> idArraySplit(String idArrayStr) {
        List<Long> idList = null;
        if (CommonUtil.isNotBlank(idArrayStr)) {

            String[] idArray = idArrayStr.split(",");

            if (ArrayUtils.isNotEmpty(idArray)) {
                idList = new ArrayList<>();

                for (String oneId : idArray) {

                    Long idLong = Long.parseLong(oneId.trim());

                    idList.add(idLong);
                }
            }
        }

        return idList;
    }

    /**
     * 拆分 id 字符串
     * @param idArrayStr
     * @return
     */
    public static Set<Long> splitIdArraySet(String idArrayStr) {
        Set<Long> idSet = null;
        if (CommonUtil.isNotBlank(idArrayStr)) {

            String[] idArray = idArrayStr.split(",");

            if (ArrayUtils.isNotEmpty(idArray)) {
                idSet = new HashSet<>();

                for (String oneId : idArray) {

                    Long idLong = Long.parseLong(oneId.trim());

                    idSet.add(idLong);
                }
            }
        }

        return idSet;
    }

    /**
     * 校验正则表达式
     * @param regex
     * @param content
     * @return
     */
    public static boolean regCheck(String regex, String content){
        boolean isMatch = Pattern.matches(regex, content);
        return isMatch;
    }

    /**
     * 判断是否相同
     * @param o1
     * @param o2
     * @param allowNull 为空时，是否一样
     * @return
     */
    public static boolean objEquels(Object o1, Object o2, boolean allowNull) {
        boolean result = false;
        if (allowNull) {
            if (o1 == null && o2 == null) {
                return true;
            }
        }

        if (o1 != null && o2 != null) {
            result = o1.equals(o2);
        }

        return result;
    }

    /**
     * 解析url参数
     * @param url
     * @return
     */
    public static Map<String, String> getUrlParameterMap(String url) {

        String params = url.substring(url.indexOf("?") + 1);

        String[] keyValueArray = params.split("&");

        Map<String, String> paramMap = new HashMap<>();

        for(String oneKeyValue : keyValueArray){
            if(CommonUtil.isNotBlank(oneKeyValue)) {

                int eqIndex = oneKeyValue.indexOf("=");

                if(eqIndex > -1) {
                    String oneKey = oneKeyValue.substring(0, eqIndex);
                    String oneValue = oneKeyValue.substring(eqIndex + 1);

                    try {
                        oneKey = URLDecoder.decode(oneKey, "UTF-8");
                        oneValue = URLDecoder.decode(oneValue, "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        throw CustomException.createException(e);
                    }

                    paramMap.put(oneKey, oneValue);
                }
            }
        }

        return paramMap;
    }

    /**
     * 替换url 参数
     * @param url
     * @param key
     * @param value
     * @return
     */
    public static String replaceUrlParameter(String url, String key, String value){

        int indexStart = url.indexOf(key + "=");

        if(indexStart <= -1){
            return url;
        }

        int indexEnd = url.indexOf("&", indexStart);

        if(indexEnd <= -1){
            indexEnd = url.length();
        }

        try {
            value = URLEncoder.encode(value, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw CustomException.createException(e);
        }

        String urlBefore = url.substring(0, indexStart);

        String urlMiddle = key + "=" + value;

        String urlAfter = url.substring(indexEnd);

        String urlResult = urlBefore + urlMiddle + urlAfter;

        return urlResult;
    }

    /**
     * 是否全部为真
     * @param flags
     * @return
     */
    public static boolean allTrue(Boolean ... flags){

        boolean booleanResult = true;

        if(flags != null) {
            for (Boolean oneBolean : flags) {
                if(oneBolean == null || !oneBolean){
                    booleanResult = false;
                    break;
                }
            }
        }

        return booleanResult;
    }

    public static String getStringValue(String value, String defaultValue){
        if(value != null){
            return value;
        }else{
            return defaultValue;
        }
    }

    /**
     * 计算页数
     * @param totalSize
     * @param pageSize
     * @return
     */
    public static int getPageCount(int totalSize, int pageSize){

        if(pageSize == 0){
            throw CustomException.createException(ResponseEnum.request_error.info());
        }

        int pageCount = new BigDecimal(totalSize).divide(new BigDecimal(pageSize), 0, RoundingMode.UP).intValue();

        return pageCount;
    }

    /**
     * list 分页
     * @param dataList
     * @param pageNumber 1开始
     * @param pageSize
     * @param <T>
     * @return
     */
    public static <T> List<T> paggerList(List<T> dataList, int pageNumber, int pageSize){
        int size = dataList.size();

        int start = (pageNumber - 1) * pageSize;

        if(start >= size){
            return null;
        }

        int end = start + pageSize;

        if(end >= dataList.size()){
            end = dataList.size();
        }

        List<T> onePageList = new ArrayList<>();

        for(int i=start; i<end; i++){
            T oneObj = dataList.get(i);

            onePageList.add(oneObj);
        }
        return onePageList;
    }

    /**
     * 循环休眠，测试用，正式不建议使用
     * @param millis
     * @param loopCount
     */
    public static void loopSleep(long millis, int loopCount){

        for(int i= loopCount; i > 0 ; i--){
            log.info(" ==== 休眠 " + millis + " 毫秒，剩余 " + i + " 次!");

            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                log.error("", e);
            }

        }

    }

    public static boolean isNotBlank(CharSequence cs) {
        return !isBlank(cs);
    }

    public static int length(CharSequence cs) {
        return cs == null ? 0 : cs.length();
    }

    public static boolean isBlank(CharSequence cs) {
        int strLen = length(cs);
        if (strLen == 0) {
            return true;
        } else {
            for(int i = 0; i < strLen; ++i) {
                if (!Character.isWhitespace(cs.charAt(i))) {
                    return false;
                }
            }
            return true;
        }
    }

    public static void printSpendTime(long startTime){

        long endTime = System.currentTimeMillis();

        long spend = (endTime - startTime);

        log.info("#### spend_time : " + spend + " Millis!");

    }

    public static boolean isValidIp(String ip) {
        String partReg = "((25[0-5])|(2[0-4]\\d)|(1\\d{2})|([1-9]\\d)|([0-9]))";

        String ipReg = "^" + partReg + "\\." + partReg + "\\." + partReg + "\\." + partReg + "$";

        boolean match = regMatch(ipReg, ip);

        return match;
    }

    /**
     * 获取远端Ip地址
     * @param request
     * @return
     */
    public static String getIpAdrress(HttpServletRequest request) {
        String unknown = "unknown";
        String ipStr = "";
        try {
            ipStr = request.getHeader("X-Forwarded-For");

            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getHeader("X-Real-IP");
            }
            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isBlank(ipStr) || unknown.equalsIgnoreCase(ipStr)) {
                ipStr = request.getRemoteAddr();
            }

            if(StringUtils.isNotEmpty(ipStr) && !unknown.equalsIgnoreCase(ipStr)){
                //多次反向代理后会有多个ip值，第一个ip才是真实ip
                //为防止暴露服务器ip
                /*
                if(ipStr.equals("127.0.0.1") || ipStr.equalsIgnoreCase("localhost")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();

                        ipStr = inet.getHostAddress();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                */
                if(ipStr.equals("0.0.0.0")) {
                    ipStr = "127.0.0.1";
                }

                int index = ipStr.indexOf(",");
                if(index != -1) {
                    ipStr = ipStr.substring(0, index);
                }
            }

            if(!isValidIp(ipStr)){
                ipStr = "";
            }
        } catch (Exception e) {
            log.error("", e);
        }

        return ipStr;
    }

    /**
     * 获取远端Ip地址
     * @return
     */
    public static String getIpAdrress(){
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();

        return getIpAdrress(request);
    }

    /**
     * 是否有空字符串
     * @param valueArray
     * @return
     */
    public static boolean hasBlankStr(String ... valueArray){

        boolean result = false;

        if(ArrayUtils.isNotEmpty(valueArray)){
            for(String oneValue : valueArray){
                if(StringUtils.isBlank(oneValue)){
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {

        for(int i=0; i<20; i++){

            String uniqueKey = CommonUtil.getUniqueKey();

            log.info(uniqueKey);
        }

        log.info("aaa");

        log.info("finished ... ");
    }

}
